#include "CWaypointCamera.h"
#include "CGlobals.h"
#include "CLevelEditor.h"
#include "CUserInterface.h"
#include "DoUndoCommands.h"
#include <cmath>

extern CGlobals* g;
extern CLevelEditor* editor;
extern CUserInterface* ui;

const float cameraHeight = 1.5f;

CWaypointCameraAnimator::CWaypointCameraAnimator (ICameraSceneNode* camera)
{
	m_camera = camera;
	m_waypoint = -1;
	m_rightMousePressed = false;
	m_wheelPressed = false;
}

bool CWaypointCameraAnimator::isEventReceiverEnabled () const
{
	return true;
}

bool CWaypointCameraAnimator::OnEvent (const SEvent& event)
{
	switch (event.EventType)
	{
		case EET_KEY_INPUT_EVENT:
			if (event.KeyInput.PressedDown)
			{
				switch (event.KeyInput.Key)
				{
					case KEY_LEFT:
					{
						setWaypoint(editor->getPrevWaypoint(m_waypoint));
						if (CSceneObject* wp = editor->getSceneObjectFromID(m_waypoint))
							editor->setSelectedNode(wp->m_SceneNode);
						break;
					}

					case KEY_RIGHT:
					{
						setWaypoint(editor->getNextWaypoint(m_waypoint));
						if (CSceneObject* wp = editor->getSceneObjectFromID(m_waypoint))
							editor->setSelectedNode(wp->m_SceneNode);
						break;
					}
				}
			}
			break;
		
		case EET_MOUSE_INPUT_EVENT:
		{
			switch (event.MouseInput.Event)
			{
				case EMIE_MMOUSE_PRESSED_DOWN:
					m_wheelPressed = true;
					m_mouseStartPos = position2di(event.MouseInput.X, event.MouseInput.Y);
					m_startDistance = getAttribute("CameraDistance");
					break;

				case EMIE_MMOUSE_LEFT_UP:
					if (m_wheelPressed)
					{
						m_wheelPressed = false;
						float newDistance = getAttribute("CameraDistance");
						setAttribute("CameraDistance", m_startDistance);
						editor->AddCommand(new CSetFloatAttributeCommand(m_waypoint, "CameraDistance", newDistance));
					}
					break;

				case EMIE_RMOUSE_PRESSED_DOWN:
					m_rightMousePressed = true;
					m_mouseStartPos = position2di(event.MouseInput.X, event.MouseInput.Y);
					m_startAngleX = getAttribute("CameraRotationX");
					m_startAngleY = getAttribute("CameraRotationY");
					setWaypoint(m_waypoint);
					break;

				case EMIE_RMOUSE_LEFT_UP:
					if (m_rightMousePressed)
					{
						m_rightMousePressed = false;
						float newAngleX = getAttribute("CameraRotationX");
						float newAngleY = getAttribute("CameraRotationY");
						setAttribute("CameraRotationX", m_startAngleX);
						setAttribute("CameraRotationY", m_startAngleY);
						CMultiCommand* multi = new CMultiCommand;
						IDoUndoCommand* rotX = new CSetFloatAttributeCommand(m_waypoint, "CameraRotationX", newAngleX);
						IDoUndoCommand* rotY = new CSetFloatAttributeCommand(m_waypoint, "CameraRotationY", newAngleY);
						multi->addCommand(rotX);
						multi->addCommand(rotY);
						multi->setDescription(L"set waypoint camera rotation");
						rotX->drop();
						rotY->drop();
						editor->AddCommand(multi);
					}
					break;

				case EMIE_MOUSE_MOVED:
					if (m_rightMousePressed)
					{
						position2di mouse = position2di(event.MouseInput.X, event.MouseInput.Y);
						position2di diff = mouse - m_mouseStartPos;
						setAttribute("CameraRotationY", m_startAngleY - 0.25f * diff.X);
						setAttribute("CameraRotationX", m_startAngleX + 0.25f * diff.Y);
						setWaypoint(m_waypoint);
					}
					if (m_wheelPressed)
					{
						position2di mouse = position2di(event.MouseInput.X, event.MouseInput.Y);
						position2di diff = mouse - m_mouseStartPos;
						setAttribute("CameraDistance", m_startDistance + 0.25f * diff.Y);
						setWaypoint(m_waypoint);
					}
					break;
			}
			break;
		}
	}

	// if nobody processed the event, return false
	return false;
}

void CWaypointCameraAnimator::animateNode (ISceneNode* node, u32 timeMs)
{
	setWaypoint(m_waypoint);
}

void CWaypointCameraAnimator::setWaypoint (int wp)
{
	CSceneObject* obj = editor->getSceneObjectFromID(wp);
	if (!obj) return;

	m_waypoint = wp;

	vector3df target = obj->m_SceneNode->getPosition();
	target.Y += cameraHeight;
	m_camera->setTarget(target);

	float dist = obj->m_UserData->getAttributeAsFloat("CameraDistance");
	vector3df dir(dist, 0, 0);
	float angleX = obj->m_UserData->getAttributeAsFloat("CameraRotationX");
	float angleY = obj->m_UserData->getAttributeAsFloat("CameraRotationY");

	int first = wp;
	int next = editor->getNextWaypoint(wp);
	if (next == -1)
	{
		next = wp;
		first = editor->getPrevWaypoint(wp);
	}
	if (first != -1 && next != -1)
	{
		vector3df p1 = editor->getSceneObjectFromID(first)->m_SceneNode->getPosition();
		vector3df p2 = editor->getSceneObjectFromID(next)->m_SceneNode->getPosition();
		vector3df diff = p2 - p1;
		angleY += (std::atan2(diff.Z, diff.X) - PI/2) * 180/PI;
	}

	dir.rotateXYBy(angleX);
	dir.rotateXZBy(angleY);

	m_camera->setPosition(target + dir);

	m_camera->updateAbsolutePosition();
}

int CWaypointCameraAnimator::getWaypoint () const
{
	return m_waypoint;
}

void CWaypointCameraAnimator::setAttribute (const c8* attribute, float value)
{
	if (CSceneObject* wp = editor->getSceneObjectFromID(m_waypoint))
	{
		wp->m_UserData->setAttribute(attribute, value);
		ui->updateProperties();
	}
}

void CWaypointCameraAnimator::addToAttribute (const c8* attribute, float value)
{
	if (CSceneObject* wp = editor->getSceneObjectFromID(m_waypoint))
	{
		wp->m_UserData->setAttribute(attribute, wp->m_UserData->getAttributeAsFloat(attribute) + value);
		ui->updateProperties();
	}
}

float CWaypointCameraAnimator::getAttribute (const c8* attribute)
{
	if (CSceneObject* wp = editor->getSceneObjectFromID(m_waypoint))
	{
		return wp->m_UserData->getAttributeAsFloat(attribute);
	}
	return 0.0f;
}
